<!--
 * Copyright ©
 * #  
 * @author: zw
 * @date: 2023-02-24 
 -->

<template>
  <layout>
    <template #header> <control @command="command" /></template>

    <template #asider> <toolbar /></template>
    <template #panel> <property ref="panel" v-bind="{ group, node, edge, editor }" @change-group="changeGroup" @change-node="changeNode" @change-edge="changeEdge" @align-node="alignNode" /></template>

    <div class="reactive h-full" @dragover.prevent @dragstart.prevent @drop="drop">
      <div class="h-full" ref="flowEditor"></div>

      <textarea-content v-if="edit.mode" v-model="edit.text" @edit-blur="updateText" v-bind="$data" />
    </div>
  </layout>

  <el-dialog v-model="dialog.visible" title="JSON信息" width="35%">
    <el-form :model="dialog.form" label-width="120px">
      <el-form-item label="JSON对象信息">
        <el-input v-model="dialog.form.jsonstr" type="textarea" :rows="8" />
      </el-form-item>
    </el-form>

    <template #footer>
      <el-button @click="dialog.visible = false">取消</el-button>
      <el-button type="primary" @click="JSONSubmit"> 确定 </el-button>
    </template>
  </el-dialog>
</template>

<script>
import layout from "@/components/layout";
import control from "@/views/control";
import toolbar from "@/views/toolbar";
import property from "@/views/property";
import FlowEditor from "@/flow/flowEditor";
import * as zrender from "zrender";
import textarea from "@/views/textarea";
import json from "@/assets/data/新人转正流程.json";

export default {
  name: "App",
  components: { layout, control, toolbar, textareaContent: textarea, property },
  data() {
    return {
      dialog: {
        visible: false,
        form: {
          jsonstr: "",
        },
      },
      action: "",
      editor: null,
      node: null,
      edge: null,
      group: null,
      nodes: [],
      edit: {
        status: "",
        mode: false,
        text: "",
        top: "",
        left: "",
        width: "",
        height: "",
      },
    };
  },

  mounted() {
    const editor = new FlowEditor(this.$refs.flowEditor);

    editor.$on("selectNode", (e) => {
      this.updateText();
      this.action = "selectNode";
      this.node = e.node;
    });

    editor.$on("selectNodes", (e) => {
      const _selectNodes = this.editor.getSelectNodes();
      if (!e && _selectNodes.length === 0) {
        this.action = "";
        this.node = null;
        this.group = null;
        this.edge = null;
        return;
      }

      if (e && e.nodes.length > 1) {
        this.action = "selectNodes";
        this.node = null;
        this.group = null;
        this.edge = null;
        this.nodes = [...e.nodes];
        return;
      }

      if (_selectNodes.length === 1) {
        this.action = "selectNode";
        this.group = null;
        this.edge = null;
        this.node = _selectNodes.shift();
        return;
      }

      if (_selectNodes.length > 1) {
        this.action = "selectNodes";
        this.node = null;
        this.group = null;
        this.edge = null;
        this.nodes = [..._selectNodes];
      }
    });

    editor.$on("selectGroup", (e) => {
      this.updateText();
      this.action = "selectGroup";
      this.group = e.group;
    });

    editor.$on("selectEdge", (e) => {
      this.updateText();
      this.edge = e.edge;
      this.action = "selectEdge";
      this.editor.status = "selectEdge";
    });

    editor.$on("undoredo", () => {
      this.$refs.panel.updateMiniMap();
    });

    editor.$on("edit", (e) => {
      const { position } = this.editor;
      const { status, text } = e;
      const group = new zrender.Group();
      this.edit.mode = true;
      this.edit.status = status;
      switch (status) {
        case "editNode":
          {
            const rect = group.getBoundingRect([e.node]);
            this.edit.left = rect.x + position[0];
            this.edit.top = rect.y + position[1];
            this.edit.width = rect.width;
            this.edit.height = rect.height;
            this.edit.text = text;
            this.node = e.node;
            this.group = null;
            this.edge = null;
          }
          break;

        case "editGroup":
          {
            const groupRect = group.getBoundingRect([this.group.groupHead]);
            const rect = group.getBoundingRect([this.group]);
            this.edit.left = rect.x + position[1];
            this.edit.top = rect.y + position[1];
            this.edit.width = groupRect.width;
            this.edit.height = groupRect.height;
            this.edit.text = text;
            this.group = e.group;
            this.node = null;
            this.edge = null;
          }
          break;

        case "editEdge":
          {
            const edgedata = e.edge.getData();
            const { text, cpx } = edgedata;

            if (text) {
              const rect = group.getBoundingRect([e.edge.relateText]);
              this.edit.left = rect.x + position[0];
              this.edit.top = rect.y + position[1];
              this.edit.width = rect.width;
              this.edit.height = rect.height;
              this.edit.text = text;
            } else {
              this.edit.left = cpx.x1 + position[0];
              this.edit.top = cpx.y1 + position[1];
              this.edit.width = 80;
              this.edit.height = 20;
            }

            this.edge = e.edge;
            this.node = null;
            this.group = null;
          }
          break;
      }
    });

    editor.$on("clearSelectItems", (e) => {
      if (this.edit.status) {
        this.edit.mode = false;
        return;
      }
      this.group = null;
      this.node = null;
      this.edge = null;
    });

    this.editor = editor;

    this.renderData(json);
  },

  methods: {
    renderData(data) {
      this.editor.clearHistory();
      this.editor.refreshHistory();
      this.editor.init(data);
    },

    JSONSubmit() {
      const { form } = this.dialog;
      try {
        const data = JSON.parse(form.jsonstr);
        this.renderData(data);
        this.dialog.visible = false;
      } catch (err) {
        console.error(err);
        this.$message.error("JSON格式错误");
      }
    },

    changeNode(payload) {
      this.$nextTick(() => {
        const selectNodes = payload.nodes || this.editor.getSelectNodes();
        this.editor.execute("changeNode", payload, selectNodes);
      });
    },

    changeGroup(payload) {
      this.$nextTick(() => {
        this.editor.execute("changeGroup", payload);
      });
    },

    changeEdge(payload) {
      this.$nextTick(() => {
        this.editor.execute("changeEdge", payload);
      });
    },

    alignNode(payload) {
      const { align } = payload;
      this.editor[align] && this.editor[align]();
    },

    drop(e) {
      const shape = e.dataTransfer.getData("addShape");

      try {
        const data = JSON.parse(shape);
        data.x = e.offsetX + 0.5;
        data.y = e.offsetY + 0.5;
        this.editor.createShape(data);
      } catch (err) {
        console.error(err);
      }
    },

    command(ctrl) {
      switch (ctrl) {
        case "undo":
          this.editor.undo();
          break;

        case "redo":
          this.editor.redo();
          break;

        case "copy":
          if (this.action === "selectNode" || this.action === "selectNodes") {
            this.editor.copy("node", this.editor.getSelectNodes());
          } else if (this.action === "selectGroup") {
            this.editor.copy("group", this.group);
          }
          break;

        case "delete":
          if (this.action === "selectNode") {
            this.editor.execute("deleteNode", { node: this.node });
          } else if (this.action === "selectNodes") {
            this.editor.execute("deleteNodes", { nodes: this.nodes });
          } else if (this.action === "selectGroup") {
            this.editor.execute("deleteGroup", { group: this.group });
          } else if (this.action === "selectEdge") {
            this.editor.execute("deleteEdge", { edge: this.edge });
          }
          break;

        case "selectFrame":
          this.editor.$emit("selectFrame");
          break;

        case "createGroup":
          this.editor.createGroup();
          break;

        case "ungroup":
          if (!this.action === "selectGroup") return;
          this.editor.execute("ungroup", { group: this.group });
          break;

        case "JSONString":
          this.dialog.visible = true;
          break;

        default:
          break;
      }
    },

    updateText() {
      const { status, text } = this.edit;

      if (status === "editNode") {
        this.editor.execute("changeNode", { style: { text }, nodes: [this.node].filter(Boolean) }, [this.node].filter(Boolean));
      }

      if (status === "editGroup") {
        this.editor.execute("changeGroup", { mark: "head", style: { text }, group: this.group });
      }

      if (status === "editEdge") {
        this.editor.execute("changeEdge", { mark: "text", style: { text }, edge: this.edge });
      }

      this.edit.mode = false;
      this.edit.status = "";
      this.edit.text = "";
      this.node = null;
      this.group = null;
      this.edge = null;
    },
  },
};
</script>

<style lang="scss" scoped></style>
